package com.doubletick.proxy.ipidea.manager.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.doubletick.common.core.domain.AjaxResult;
import com.doubletick.common.core.domain.entity.SysUser;
import com.doubletick.common.core.redis.RedisLock;
import com.doubletick.common.utils.SecurityUtils;
import com.doubletick.proxy.dto.FlowCountDto;
import com.doubletick.proxy.dto.IdNameDto;
import com.doubletick.proxy.ipidea.async.AsyncProxyFactory;
import com.doubletick.proxy.ipidea.async.AsyncProxyManager;
import com.doubletick.proxy.ipidea.cache.AccountDayFlowCache;
import com.doubletick.proxy.ipidea.cache.UsernameCache;
import com.doubletick.proxy.ipidea.domain.ProxyAccount;
import com.doubletick.proxy.ipidea.domain.ProxyAccountFlow;
import com.doubletick.proxy.ipidea.domain.ProxyFlowChargeHistory;
import com.doubletick.proxy.ipidea.enums.ChargeType;
import com.doubletick.proxy.ipidea.enums.OperateResult;
import com.doubletick.proxy.ipidea.manager.IProxyAccountManager;
import com.doubletick.proxy.ipidea.service.IProxyAccountFlowService;
import com.doubletick.proxy.ipidea.service.IProxyAccountService;
import com.doubletick.proxy.ipidea.service.IProxyFlowChargeHistoryService;
import com.doubletick.proxy.ipidea.util.FlowChargeDto;
import com.doubletick.proxy.ipidea.util.ProxyAccountUtil;
import com.doubletick.proxy.ipidea.util.dto.IpIdeaResponseDto;
import com.doubletick.proxy.ipidea.util.dto.account.ProxyAccountEdit;
import com.doubletick.proxy.ipidea.util.dto.account.ProxyAccountResult;
import com.doubletick.proxy.ipidea.util.dto.flow.FlowUserRecord;
import com.doubletick.system.service.ISysUserService;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class ProxyAccountManager implements IProxyAccountManager {
    @Resource
    private ISysUserService userService;
    @Resource
    private IProxyAccountService proxyAccountService;
    @Resource
    private IProxyFlowChargeHistoryService flowChargeHistoryService;

    @Resource
    private AccountDayFlowCache accountFlowCache;

    @Resource
    private IProxyAccountFlowService accountFlowService;

    @Resource
    private IProxyAccountFlowService proxyAccountFlowService;

    public static final Long defaultDeptId = 110L;
    public static final Long defaultRoleId = 3L;

    @Autowired
    private RedisLock redisLock;

    @SneakyThrows
    @Override
    public AjaxResult addAccount(String username, String password, String limitFlow, String remark) {
        if (StrUtil.isEmpty(username) || StrUtil.isEmpty(password)) {
            return AjaxResult.error("用户名和密码参数不能为空");
        }
        if (StrUtil.isEmpty(limitFlow) || !NumberUtil.isLong(limitFlow) || Long.parseLong(limitFlow) < 0) {
            return AjaxResult.error("流量设置无效");
        }
        if (Long.parseLong(limitFlow) == 0) {
            return AjaxResult.error("测试报错");
        }

        SysUser checkUser = new SysUser();
        checkUser.setUserName(username);
        boolean unique = userService.checkUserNameUnique(checkUser);
        if (!unique) {
            return AjaxResult.error("用户名已存在");
        }

        String lockKey = "addAccount";
        //三方耗时较长，这里用锁住，一次只能操作一个
        boolean lock = redisLock.tryLock(lockKey, 30, TimeUnit.SECONDS);
        if (!lock) {
            return AjaxResult.error("添加失败，请稍后再试");
        }
        try {
            String thirdAccount = RandomUtil.randomString(RandomUtil.BASE_CHAR, 4).toLowerCase() + "_" + RandomUtil.randomNumbers(4);
            String thirdProxyPassword = RandomUtil.randomString(6);
//        String limitFlow = "1";
            //三方插入
            AjaxResult result = thirdInsert(thirdAccount, thirdProxyPassword, limitFlow);
            if (!result.isSuccess()) {
                return result;
            }

            SysUser user = new SysUser();
            user.setUserName(username);
            user.setNickName(username);
            user.setDeptId(defaultDeptId);
            user.setRoleIds(Arrays.asList(defaultRoleId).toArray(new Long[0]));
            user.setPassword(SecurityUtils.encryptPassword(password));
            user.setCreateBy(SecurityUtils.getUsername());
            userService.insertUser(user);

            ProxyAccount proxyAccount = new ProxyAccount();
            //这个id，暂时还没有 TODO
//        proxyAccount.setProxyId("-1111");
            proxyAccount.setAccount(thirdAccount);
            proxyAccount.setPassword(thirdProxyPassword);
            proxyAccount.setLimitFlow(limitFlow);
            proxyAccount.setUserId(user.getUserId());
            proxyAccount.setRemark(remark);
            proxyAccountService.insertProxyAccount(proxyAccount);


            // 插入流量历史
            saveFlowHistory(limitFlow, user.getUserId());

//        //需要异步查询，绑定对应的id
            log.info("开始异步绑定 proxyId:" + proxyAccount.getAccount());
            AsyncProxyManager.me().execute(AsyncProxyFactory.bindProxyId(proxyAccount));
            return AjaxResult.success();
        } finally {
            redisLock.unlock(lockKey);
        }
    }

    private void saveFlowHistory(String limitFlow, Long userId) {
        ProxyFlowChargeHistory history = new ProxyFlowChargeHistory();
        history.setChargeType(ChargeType.charge);
        history.setFlow(Long.parseLong(limitFlow));
//        history.setProxyId(); TODO 同上
        history.setOperateResult(OperateResult.SUCCESS);
        history.setMsg("创建账号分配流量:" + limitFlow + "M");
        history.setCreateUserId(SecurityUtils.getUserId());
        history.setAfterFlow(history.getFlow());
        history.setUserId(userId);
        flowChargeHistoryService.save(history);
    }

    private AjaxResult thirdInsert(String proxyAccount, String proxyPassword, String limitFlow) {
        IpIdeaResponseDto dto = ProxyAccountUtil.add(proxyAccount, proxyPassword, limitFlow);
        if (dto.isSuccess()) {
            return AjaxResult.success();
        }
        return AjaxResult.error("添加失败:" + dto.getMsg());
    }

    @Override
    public AjaxResult chargeFlow(FlowChargeDto chargeFlow) {
        chargeFlow.setChargeType(ChargeType.charge);
        chargeFlow.setLabel("充值");
        return operateFlow(chargeFlow);
    }


    @Override
    public AjaxResult reduceFlow(FlowChargeDto chargeFlow) {
        chargeFlow.setChargeType(ChargeType.reduce);
        chargeFlow.setLabel("扣除");
        return operateFlow(chargeFlow);
    }

    private AjaxResult operateFlow(FlowChargeDto chargeFlow) {
        if (!NumberUtil.isLong(chargeFlow.getFlow()) || Long.parseLong(chargeFlow.getFlow()) <= 0) {
            return AjaxResult.error("流量无效");
        }
        // 获取当前
        ProxyAccount proxyAccount = permissionsQuery(chargeFlow);
        if (proxyAccount == null) {
            return AjaxResult.error("权限不足");
        }
        BigDecimal afterFlow = null;
        if (Objects.equals(ChargeType.charge, chargeFlow.getChargeType())) {
            afterFlow = new BigDecimal(proxyAccount.getLimitFlow()).add(new BigDecimal(chargeFlow.getFlow()));
        } else {
            afterFlow = new BigDecimal(proxyAccount.getLimitFlow()).subtract(new BigDecimal(chargeFlow.getFlow()));
        }

        IpIdeaResponseDto dto = remoteEdit(proxyAccount, afterFlow);

        //记录历史
        saveFlowChargeHistory(chargeFlow, proxyAccount, dto, afterFlow);

        if (dto.isSuccess()) {
            proxyAccount.setLimitFlow(afterFlow.toPlainString());
            proxyAccountService.updateById(proxyAccount);
        }

        if (dto.isSuccess()) {
            return AjaxResult.success();
        }
        return AjaxResult.error(chargeFlow.getLabel() + "失败" + dto.getMsg());
    }

    private void saveFlowChargeHistory(FlowChargeDto chargeFlow, ProxyAccount proxyAccount, IpIdeaResponseDto dto, BigDecimal afterFlow) {
        ProxyFlowChargeHistory history = new ProxyFlowChargeHistory();
        history.setChargeType(chargeFlow.getChargeType());
        history.setBeforeFlow(Long.parseLong(proxyAccount.getLimitFlow()));
        history.setAfterFlow(dto.isSuccess() ? afterFlow.longValue() : Long.parseLong(proxyAccount.getLimitFlow()));
        history.setCreateUserId(SecurityUtils.getUserId());
        history.setMsg(JSON.toJSONString(dto));
        history.setOperateResult(dto.isSuccess() ? OperateResult.SUCCESS : OperateResult.FAIL);
        history.setUserId(proxyAccount.getUserId());
        history.setProxyId(proxyAccount.getProxyId());
        history.setFlow(Long.parseLong(chargeFlow.getFlow()));
        flowChargeHistoryService.save(history);
    }

    private static IpIdeaResponseDto remoteEdit(ProxyAccount proxyAccount, BigDecimal afterFlow) {
        ProxyAccountEdit edit = new ProxyAccountEdit();
        edit.setId(proxyAccount.getProxyId());
        edit.setAccount(proxyAccount.getAccount());
        edit.setPassword(proxyAccount.getPassword());
        edit.setState(proxyAccount.getState());
        edit.setLimit_flow(afterFlow.toPlainString());
        IpIdeaResponseDto dto = ProxyAccountUtil.edit(edit);
        return dto;
    }

    private ProxyAccount permissionsQuery(FlowChargeDto chargeFlow) {
        if (chargeFlow.getProxyAccountId() == null) {
            return null;
        }
        ProxyAccount query = new ProxyAccount();
        query.setId(chargeFlow.getProxyAccountId());
        List<ProxyAccount> list = proxyAccountService.selectProxyAccountList(query);
        return CollectionUtils.isEmpty(list) ? null : list.get(0);
    }

    @Override
    public boolean saveList(ProxyAccountResult account, List<FlowUserRecord> flowList) {
        String proxyId = account.getId();
        ProxyAccount proxyAccount = proxyAccountService.selectByProxyId(proxyId);
        if (proxyAccount == null) {
            return false;
        }
        for (FlowUserRecord record : flowList) {
            BigDecimal globalFlow = new BigDecimal(record.getGlobal());
            if (BigDecimal.ZERO.compareTo(globalFlow) == 0) {
                continue;
            }
            //判断缓存
            String dateStr = DateUtil.formatDateTime(new Date(Integer.parseInt(record.getDay()) * 1000L)).substring(0, 13);

            String cacheKey = getCacheKey(dateStr, proxyId);
            BigDecimal cacheValue = accountFlowCache.get(cacheKey);
            if (cacheValue != null && globalFlow.compareTo(cacheValue) == 0) {
                log.info("缓存命中,跳过:{}:{}:{}:{}", proxyId, account.getAccount(), dateStr, globalFlow.toPlainString());
                continue;
            }
            ProxyAccountFlow accountFlow = new ProxyAccountFlow();
            accountFlow.setUserId(proxyAccount.getUserId());
            accountFlow.setProxyId(proxyId);
            if (NumberUtil.isLong(record.getDay())) {
                accountFlow.setDay(Long.parseLong(record.getDay()));
                accountFlow.setDataTime(new Date(accountFlow.getDay() * 1000));
            }
            if (NumberUtil.isNumber(record.getGlobal())) {
                accountFlow.setGlobal(new BigDecimal(record.getGlobal()));
            }
            if (accountFlow.getDataTime().getTime() <= System.currentTimeMillis()) {
                log.info("更新数据:{}:{}:{}:{}", proxyId, account.getAccount(), dateStr, globalFlow);
                accountFlowService.saveOrUpdate(accountFlow);
                //加入缓存
                accountFlowCache.put(cacheKey, globalFlow);
            }
        }
        return true;
    }

    private String getCacheKey(String day, String proxyId) {
        return StrUtil.format("{}_{}", day.replaceAll(" ", ":"), proxyId);
    }

    public static void main(String[] args) {
        Long x = 1720454400L;
        System.out.println(new Date(x));
        System.out.println(new Date(x * 1000L));
        System.out.println(LocalDate.now());
    }

    @Override
    public AjaxResult update(ProxyAccount proxyAccount) {
        ProxyAccount history = proxyAccountService.selectProxyAccountById(proxyAccount.getId());
        if (history == null) {
            return AjaxResult.error("账号不存在");
        }
        proxyAccount.setLimitFlow(history.getLimitFlow());
        proxyAccount.setProxyId(history.getProxyId());

        String newStr = StrUtil.format("{}_{}_{}", proxyAccount.getAccount(), proxyAccount.getPassword(), proxyAccount.getState());
        String oldStr = StrUtil.format("{}_{}_{}", history.getAccount(), history.getPassword(), history.getState());
        if (!Objects.equals(oldStr, newStr)) {
            // 修改三方
            IpIdeaResponseDto dto = remoteEdit(proxyAccount, new BigDecimal(proxyAccount.getLimitFlow()));
            if (!dto.isSuccess()) {
                return AjaxResult.error("修改失败" + dto.getMsg());
            }
        }
        if (StrUtil.isEmpty(proxyAccount.getRemark())) {
            proxyAccount.setRemark("");
        }
        proxyAccountService.updateProxyAccount(proxyAccount);
        return AjaxResult.success();
    }

    @Override
    public AjaxResult proxyAccountList(ProxyAccount proxyAccount) {
        List<IdNameDto> list = proxyAccountService.proxyAccountList(proxyAccount);
        return AjaxResult.success(list);
    }

    @Override
    public AjaxResult userList(ProxyAccount proxyAccount) {
        List<IdNameDto> list = proxyAccountService.userList(proxyAccount);
        return AjaxResult.success(list);
    }

    @Override
    public AjaxResult flowCount() {
        JSONObject summary = new JSONObject();
//        //总消耗
//        ProxyAccountFlow queryFlow = new ProxyAccountFlow();
//        Object summaryAll = proxyAccountFlowService.summary(queryFlow);
//        summary.put("all", summaryAll);
//
//        //今天消耗
//        String todayStr = LocalDate.now().toString();
//        queryFlow.getParams().put("beginDataTime", todayStr);
//        queryFlow.getParams().put("endDataTime", todayStr);
//        Object summaryToday = proxyAccountFlowService.summary(queryFlow);
//        summary.put("today", summaryToday);
//
//        String yesterdayStr = LocalDate.now().minusDays(1).toString();
//        //昨天消耗
//        queryFlow.getParams().put("beginDataTime", yesterdayStr);
//        queryFlow.getParams().put("endDataTime", yesterdayStr);
//        Object summaryYesterday = proxyAccountFlowService.summary(queryFlow);
//        summary.put("yesterday", summaryYesterday);

        BigDecimal yesterday = new BigDecimal(0);
        BigDecimal today = new BigDecimal(0);
        BigDecimal computeToday = new BigDecimal(0);
        BigDecimal all = new BigDecimal(0);

        List<FlowCountDto> userList = new ArrayList<>();
        List<ProxyAccount> proxyAccountList = proxyAccountService.selectProxyAccountList(new ProxyAccount());
        for (ProxyAccount account : proxyAccountList) {
            if (BigDecimal.ZERO.compareTo(account.getConsumedFlow()) == 0) {
                continue;
            }
            all = all.add(account.getConsumedFlow());
            FlowCountDto dto = countByUser(account);
            yesterday = yesterday.add(new BigDecimal(dto.getYesterdayConsumedFlow()));
            computeToday = computeToday.add(new BigDecimal(dto.getComputeTodayFlow()));
            today= today.add(new BigDecimal(dto.getTodayConsumedFlow()));
            //在这里统计 总的消耗 今天消耗  昨天消耗 TODO
            userList.add(dto);
        }

        if (proxyAccountList.size() > 1) {
            summary.put("show", true);
        } else {
            summary.put("show", false);
        }
        summary.put("all", all);
        summary.put("today", today);
        summary.put("computeToday", computeToday);
        summary.put("yesterday", yesterday);

        JSONObject result = new JSONObject();
        result.put("summary", summary);
        result.put("userList", userList);
        return AjaxResult.success(result);
    }

    private FlowCountDto countByUser(ProxyAccount account) {
        FlowCountDto dto = new FlowCountDto();
        dto.setAccount(account.getAccount());
        dto.setUsername(usernameCache.getUsernameByUserId(account.getUserId()));
        dto.setAccountConsumedFlow(account.getConsumedFlow().toPlainString());
        String residueFlowStr;
        BigDecimal limitFlow = new BigDecimal(account.getLimitFlow());
        if (BigDecimal.ZERO.compareTo(limitFlow) == 0) {
            residueFlowStr = "不限制";
            dto.setChargeFlow("不限制");
        } else {
            dto.setChargeFlow(account.getLimitFlow());
            residueFlowStr = limitFlow.subtract(account.getConsumedFlow()).toPlainString();
        }
        dto.setResidueFlow(residueFlowStr);

        ProxyAccountFlow queryFlow = new ProxyAccountFlow();
        queryFlow.setProxyId(account.getProxyId());

        //总消耗
        Object summaryAll = proxyAccountFlowService.summary(queryFlow);
        dto.setAllConsumedFlow(String.valueOf(summaryAll));

        //   今天
        String todayStr = LocalDate.now().toString();
        queryFlow.getParams().put("beginDataTime", todayStr);
        queryFlow.getParams().put("endDataTime", todayStr);
        Object summaryToday = proxyAccountFlowService.summary(queryFlow);
        dto.setTodayConsumedFlow(String.valueOf(summaryToday));

        //昨天消耗
        String yesterdayStr = LocalDate.now().minusDays(1).toString();
        queryFlow.getParams().put("beginDataTime", yesterdayStr);
        queryFlow.getParams().put("endDataTime", yesterdayStr);
        Object summaryYesterday = proxyAccountFlowService.summary(queryFlow);
        dto.setYesterdayConsumedFlow(String.valueOf(summaryYesterday));


        // data.accountConsumedFlow-(data.allConsumedFlow-data.todayConsumedFlow))}
//        dto.setComputeTodayFlow();
        return dto;
    }

    @Resource
    private UsernameCache usernameCache;
}
